﻿using System;
using System.Diagnostics;
using Microsoft.Win32;

namespace Red.Launcher.Utilities
{
    /// <summary>
    /// How the toolkit package was installed
    /// </summary>
    public enum EInstallationType
    {
        /// <summary>
        /// Package was installed via an MSI installer
        /// </summary>
        local,

        /// <summary>
        /// Package was installed via the launcher
        /// </summary>
        managed
    }

    /// <summary>
    /// Simplifies registry access
    /// </summary>
    public class RegistryHelper
    {
        #region Properties

        /// <summary>
        /// The location under which all registry keys should be accessed
        /// </summary>
        public string Root { get; set; }

        #endregion

        #region C-Tor
        
        public RegistryHelper()
        {
        }

        #endregion C-Tor

        #region Private Methods

        /// <summary>
        /// Construct the full registry key for a particular installation type
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <returns>The constructed registry key</returns>
        private string ConstructKey( EInstallationType installType )
        {
            return Root + installType.ToString();
        }

        /// <summary>
        /// Construct the full registry key for a particular installation type
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="guid">Unique identifier of the installed package</param>
        /// <returns>The constructed registry key</returns>
        private string ConstructKey( EInstallationType installType, string guid )
        {
            return ConstructKey( installType ) + @"\" + guid;
        }

        #endregion Private Methods

        #region Public Methods

        /// <summary>
        /// Create or set a registry entry for an arbitrary path
        /// </summary>
        /// <param name="path">Registry entry path or "folder" (after HKEY_CURRENT_USER)</param>
        /// <param name="name">Registry entry name or "file"</param>
        /// <param name="value">value in the form of a <see langword="string"/></param>
        public void SetValue( string path, string name, string value )
        {
            using ( RegistryKey key = Registry.CurrentUser.CreateSubKey( path ) )
            {
                key.SetValue( name, value, RegistryValueKind.String );
            }
        }

        /// <summary>
        /// Create or set a registry entry for a particular install type
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="name">Registry entry name or "file"</param>
        /// <param name="value">value in the form of a <see langword="string"/></param>
        public void SetValue( EInstallationType installType, string name, string value )
        {
            string path = ConstructKey( installType );
            SetValue( path, name, value );
        }

        /// <summary>
        /// Create or set a registry entry for a particular install
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="guid">Unique identifier of the installed package</param>
        /// <param name="name">Registry entry name or "file"</param>
        /// <param name="value">value in the form of a <see langword="string"/></param>
        public void SetValue( EInstallationType installType, string guid, string name, string value )
        {
            string path = ConstructKey( installType, guid );
            SetValue( path, name, value );
        }

        /// <summary>
        /// Get the value of a registry entry for an arbitrary path
        /// </summary>
        /// <param name="path">Registry entry path or "folder" (after HKEY_CURRENT_USER)</param>
        /// <param name="name">Registry entry name or "file"</param>
        /// <returns>Value of the registry entry at the specified location</returns>
        public string GetValue( string path, string name )
        {
            using ( RegistryKey key = Registry.CurrentUser.OpenSubKey( path ) )
            {
                return (string)key.GetValue( name );
            }
        }

        /// <summary>
        /// Get the value of a registry entry for a particular install type
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="name">Registry entry name or "file"</param>
        /// <returns>Value of the registry entry at the specified location</returns>
        public string GetValue( EInstallationType installType, string name )
        {
            string path = ConstructKey( installType );
            return GetValue( path, name );
        }

        /// <summary>
        /// Get the value of a registry entry for a particular install
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="guid">Unique identifier of the installed package</param>
        /// <param name="name">Registry entry name or "file" </param>
        /// <returns>Value of the registry entry at the specified location</returns>
        public string GetValue( EInstallationType installType, string guid, string name )
        {
            string path = ConstructKey( installType, guid );
            return GetValue( path, name );
        }

        /// <summary>
        /// Return a list of all child keys for a particular install type
        /// </summary>
        /// <remarks>
        /// This will basically get you a list guids for all packages installed via an MSI installer or through the launcher
        /// </remarks>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <returns>Array of registry key names</returns>
        public string[] GetSubKeys( EInstallationType installType )
        {
            string path = ConstructKey( installType );
            using ( RegistryKey key = Registry.CurrentUser.OpenSubKey( path ) )
            {
                return key.GetSubKeyNames();
            }
        }

        /// <summary>
        /// Delete the root registry key "folder" (if empty)
        /// </summary>
        public void DeleteKey()
        {
            try
            {
                Registry.CurrentUser.DeleteSubKey( Root );
            }
            catch ( InvalidOperationException e )
            {
                Debug.Print( "Cannot delete registry key '{0}' because it has child keys", Root );
                Debug.Print( e.ToString() );
            }
            catch ( ArgumentException e )
            {
                Debug.Print( "Cannot delete registry key '{0}' - it does not exist", Root );
                Debug.Print( e.ToString() );
            }
        }

        /// <summary>
        ///  Delete the local/managed registry key "folder" (if empty)
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        public void DeleteKey( EInstallationType installType )
        {
            string path = ConstructKey( installType );

            try
            {
                Registry.CurrentUser.DeleteSubKey( path );
            }
            catch ( InvalidOperationException e )
            {
                Debug.Print( "Cannot delete registry key '{0}' because it has child keys", path );
                Debug.Print( e.ToString() );
            }
            catch ( ArgumentException e )
            {
                Debug.Print( "Cannot delete registry key '{0}' - it does not exist", path );
                Debug.Print( e.ToString() );
            }
        }

        /// <summary>
        /// Delete all entries relating to the specified package
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="guid">Unique identifier of the installed package</param>
        public void DeleteKeyRecursive( EInstallationType installType, string guid )
        {
            string path = ConstructKey( installType, guid );

            try
            {
                Registry.CurrentUser.DeleteSubKeyTree( path );
            }
            catch ( ArgumentException e )
            {
                Debug.Print( "Cannot recursively delete registry key '{0}' - it does not exist", path );
                Debug.Print( e.ToString() );
            }
        }

        /// <summary>
        /// Delete specified entry
        /// </summary>
        /// <param name="installType">How the toolkit package was installed</param>
        /// <param name="name">Registry entry name or "file"</param>
        public void DeleteValue( EInstallationType installType, string name )
        {
            string path = ConstructKey( installType );

            using ( RegistryKey key = Registry.CurrentUser.OpenSubKey( path, RegistryKeyPermissionCheck.ReadWriteSubTree ) )
            {
                try
                {
                    key.DeleteValue( name );
                }
                catch ( ArgumentException e )
                {
                    Debug.Print( "Cannot recursively delete registry value '{0}' - it does not exist", path );
                    Debug.Print( e.ToString() );
                }
            }
        }

        #endregion Public Methods
    }
}
